# include "XmlObject.h"
# include "XmlDealState.h"
# include "XmlItem.h"

ZDSJ::XmlObject::XmlObject(const char* _file_name){
    this->m_root = new ZDSJ::XmlItem("head");
    this->loadFile(_file_name);
}

const ZDSJ::XmlItem* ZDSJ::XmlObject::root() const{
    return this->m_root->item(0);
}

ZDSJ::XmlObject::~XmlObject(){
    delete this->m_root;
}

void ZDSJ::XmlObject::loadFile(const char* _file_name){
    FILE* file;
    errno_t err = fopen_s(&file, _file_name, "rb");
    if(err != 0){
        // std::cout << "Failed to open file" << std::endl;
        // TODO log 打开文件失败
        return;
    }
    char* buffer = new char[FILE_SIZE];
    size_t read_size = 0;
    std::stack<ZDSJ::XmlItem*> stack;
    stack.push(this->m_root);
    // stack.push_back(this->m_root);
    while(!feof(file)){
        read_size = fread_s(buffer, FILE_SIZE, 1, FILE_SIZE, file);
        if(this->dealBuffer(buffer, read_size, stack)){
            break;
        }
    }
    end:
        delete[] buffer;
        fclose(file);
}

bool ZDSJ::XmlObject::dealBuffer(const char* _buffer, size_t _size, std::stack<XmlItem*>& _stack){
    XmlItem* parent = _stack.top();
    if(parent == nullptr){
        // 仍然有数据，但栈空 
        // TODO抛出异常
        return false;
    }
    size_t tag_index = 0;
    size_t tag_len = 0;
    XmlItem* temp = nullptr;
    while(tag_index < _size){
        temp = this->findTag(_buffer, _size, tag_index);
        if(temp != nullptr){
            if (temp == this->m_root) {
                continue;
            }
            parent->addItem(temp);
            _stack.push(temp);
            parent = temp;
        }
        else {
            _stack.pop();
            if (_stack.empty()) {
                break;
            }
            parent = _stack.top();
        }
        // std::cout << tag_index << ", size: " << _size << std::endl;
    }
    return true;
}

ZDSJ::XmlItem* ZDSJ::XmlObject::findTag(const char* _buffer, size_t _size, size_t& _index){
    size_t tag_index = _index;
    // 这里不用std::string, 因为会重新分配内存
    // std::string str(_buffer + tag_index);
    // std::regex partten("<(\w+)(?:\s+\w+=\"[^\"]*\")*>");
    // std::smatch match;
    // std::regex_search(str, match, partten);
    ZDSJ::XmlItem* item = nullptr;
    // 尖括号栈
    size_t angle_brackets_stack = 0;
    // 双引号栈
    size_t quotation_marks_stack = 0;

    const char* name_key_ptr = nullptr;
    size_t name_key_len = 0;

    const char* value_ptr = nullptr;
    size_t value_len = 0;
    // 类似状态机
    ZDSJ::State state(ZDSJ::SkipSpace);
    state.setCallBack(ZDSJ::TagBegin, [&](ZDSJ::Deal_State) -> ZDSJ::Deal_State{
        ZDSJ::Deal_State result = ZDSJ::TagBegin;
        switch (_buffer[tag_index])
        {
        case '/':
            result = ZDSJ::CloseTag;
            break;
        case ' ':
            result = ZDSJ::SkipSpace;
            break;
        case '?':
            result = ZDSJ::Description;
            break;
        default:
            item = new ZDSJ::XmlItem();
            name_key_ptr = _buffer + tag_index;
            name_key_len = 1;
            result = ZDSJ::GetTagName;
            break;
        }
        return result;
    });
    state.setCallBack(ZDSJ::Description, [&](ZDSJ::Deal_State) -> ZDSJ::Deal_State {
        ZDSJ::Deal_State result = ZDSJ::Description;
        switch (_buffer[tag_index])
        {
        case '>':
            item = const_cast<ZDSJ::XmlItem*>(this->m_root);
            result = ZDSJ::TagEnd;
            break;
        default:
            break;
        }
        return result;
        });
    state.setCallBack(ZDSJ::GetTagName, [&](ZDSJ::Deal_State _last_state) -> ZDSJ::Deal_State{
        ZDSJ::Deal_State result = ZDSJ::GetTagName;
        if(item == nullptr){
            // TODO 异常
            return ZDSJ::TagEnd;
        }
        switch (_buffer[tag_index])
        {
        case ' ':
            result = ZDSJ::SkipSpace;
            item->name(name_key_ptr, name_key_len);
            name_key_ptr = nullptr;
            name_key_len = 0;
            break;
        case '>':
            result = ZDSJ::TagEnd;
            item->name(name_key_ptr, name_key_len);
            name_key_ptr = nullptr;
            name_key_len = 0;
            break;
        default:
            ++name_key_len;
            break;
        }
        return result;
    });
    state.setCallBack(ZDSJ::SkipSpace, [&](ZDSJ::Deal_State _last_state) -> ZDSJ::Deal_State{
        ZDSJ::Deal_State result = ZDSJ::SkipSpace;
        const char** temp_ptr = nullptr;
        size_t* temp_len = nullptr;
        switch (_buffer[tag_index])
        {
        case '?':
            result = ZDSJ::Description;
            break;
        case '/':
            result = ZDSJ::CloseTag;
            break;
        case '\r':
            break;
        case '\n':
            break;
        case '<':
            result = ZDSJ::TagBegin;
            break;
        case '>':
            result = ZDSJ::TagEnd;
            break;
        case ' ':
            break;
        default:
            switch (_last_state)
            {
            case ZDSJ::TagBegin:
                item = new ZDSJ::XmlItem();
                result = ZDSJ::GetTagName;
                name_key_ptr = _buffer + tag_index;
                name_key_len = 1;
                break;
            case ZDSJ::GetTagName:
                result = ZDSJ::GetKey;
                name_key_ptr = _buffer + tag_index;
                name_key_len = 1;
                break;
            case ZDSJ::SkipSpace:
                break;
            case ZDSJ::GetKey:
                result = ZDSJ::GetValue;
                value_ptr = _buffer + tag_index;
                value_len = 1;
                quotation_marks_stack = 1;
                break;
            case ZDSJ::GetValue:
                result = ZDSJ::GetKey;
                name_key_ptr = _buffer + tag_index;
                name_key_len = 1;
                break;
            default:
                break;
            }
            break;
        }
        return result;
    });
    state.setCallBack(ZDSJ::GetKey, [&](ZDSJ::Deal_State _last_state) -> ZDSJ::Deal_State{
        ZDSJ::Deal_State result = ZDSJ::GetKey;
        switch (_buffer[tag_index])
        {
        case '=':
            result = ZDSJ::SkipSpace;
            break;
        case ' ':
            break;
        default:
            ++name_key_len;
            break;
        }
        return result;
    });
    state.setCallBack(ZDSJ::GetValue, [&](ZDSJ::Deal_State _last_state) -> ZDSJ::Deal_State{
        ZDSJ::Deal_State result = ZDSJ::GetValue;
        switch (_buffer[tag_index])
        {
        case '<':
            ++angle_brackets_stack;
            break;
        case '>':
            --angle_brackets_stack;
            if(angle_brackets_stack < 0){
                result = ZDSJ::TagEnd;
            }
            break;
        case '\"':
            ++quotation_marks_stack;
            if (quotation_marks_stack == 2) {
                item->property(name_key_ptr, name_key_len, value_ptr + 1, value_len - 1);
                value_len = -1;
                name_key_len = 0;
                result = ZDSJ::SkipSpace;
                quotation_marks_stack = 0;
            }
            break;
        case '\\':
            result = ZDSJ::Transfer;
            break;
        case ' ':
            if(quotation_marks_stack == 2){
                item->property(name_key_ptr, name_key_len, value_ptr + 1, value_len - 1);
                value_len = -1;
                name_key_len = 0;
                result = ZDSJ::SkipSpace;
                quotation_marks_stack = 0;
            }
            break;
        default:
            break;
        }
        ++value_len;
        return result;
    });
    state.setCallBack(ZDSJ::CloseTag, [&](ZDSJ::Deal_State _last_state) -> ZDSJ::Deal_State {
        ZDSJ::Deal_State result = ZDSJ::CloseTag;
        switch (_buffer[tag_index])
        {
        case '>':
            result = TagEnd;
            break;
        default:
            break;
        }
        return result;
    });
    state.setCallBack(ZDSJ::TagEnd, [&](ZDSJ::Deal_State _last_state) -> ZDSJ::Deal_State{
        return ZDSJ::TagEnd;
    });
    while(tag_index < _size){
        if(ZDSJ::TagEnd == state()){
            break;
        }
        ++tag_index;
    }
    _index = tag_index + 1;
    return item;
}

void ZDSJ::XmlObject::showXmlTree(std::ostream& _out, const XmlItem* _node, short level)
{
    std::vector<std::string> keys;
    for (int i = 0; i < level * 4; ++i) {
        _out << " ";
    }
    _out << _node->name();
    _node->keys(&keys);
    if (!keys.empty()) {
        _out << ":";
        for (auto key : keys) {
            _out << " " << key << "-" << _node->property(key);
        }
    }
    _out << std::endl;
    auto items = _node->items();
    if (items == nullptr) {
        return;
    }
    for (auto item : *items) {
        this->showXmlTree(_out, item, level + 1);
    }
}

void ZDSJ::XmlObject::showXmlTree(std::ostream& _out)
{
    this->showXmlTree(_out, this->root(), 0);
}
